# STARTING THE PROGRAM

#LOADING THE MODULES
from functions import *
from userConfiguration import *
from socket import *
import rsaMod
import time, threading
import Queue

# LOAD THE GUI
from Tkinter import *
import ScrolledText

class server ( threading.Thread ):
    def __init__ ( self, client, gui):
        self.serverHost = client.serverIP
        self.serverPort = client.serverPort
        
        self.client = client
        self.gui = gui
        self.queue = self.gui.queue
        self.serverName = self.client.clientName # NAME OF THE USER
        threading.Thread.__init__ ( self )

    def run (self):
        # Set the socket parameters
        buf = 4096
        addr = ("", int(self.serverPort))
        self.gui.displayText("SERVER: STARTING SERVER AT "+str(addr))


        # Create socket and bind to address
        socketServer = socket(AF_INET,SOCK_DGRAM)
        socketServer.bind(addr)

        self.gui.displayText("SERVER: UP AND RUNNING")

        # Receive messages
        while 1:
            data,addr = socketServer.recvfrom(buf)
            
            try:
                data = tagsToDict(data)

            except:
                debug("SERVER: WHILE 1 DATA CORRUPT")
            fromHost, fromIP = addr
            debug("RECEIVED: \t"+str(data)+str(addr))
            self.processMsg(data)
                # FOR DEBUGGIN!
                #else:
                #    print "OWN HOST!!"
                #    self.processMsg(data)

        # Close socket
        self.gui.displayText("SERVER IS CLOSING")
        socketServer.close()

    def processMsg(self, msg):
        try:
            fromName = msg["fromName"]

            # CHECKING FOR CORRECT IP
            if msg["fromIP"] == "":
                return
            #print splitChar(msg["fromIP"])
            fromIP, fromPort = splitChar(msg["fromIP"])
        except TypeError:
            debug("SERVER: MSG IS CORRUPT, TypeError")
            return
        except KeyError:
            debug("SERVER: MSG IS CORRUPT, KeyError")
            return
        print "PASSED THIRD"

        # CHECK FOR TEST
        try:
            text = msg["text"]
            f = open("keys/"+fromName+".decrypt", "r")
            receiveKey = tagsToDict(f.read())
            f.close()
            if not receiveKey == "DATAERROR":
                try:
                    self.gui.displayMsg(rsaMod.decrypt(msg["text"], receiveKey), fromName)
                    debug(rsaMod.decrypt(msg["text"], receiveKey))

                    # ADD TO CONTACT LIST, IF NOT ALREADY IN IT!
                    if not fromName in self.client.contactOnlineList:
                        if len(fromName) > self.client.longestContact:
                            self.client.longestContact = len(fromName)
                        self.client.contactOnlineList[fromName] = fromIP + ":" + fromPort
                except:
                    debug("SERVER: DATA IS CORRUPT, CAN NOT decrypt")
            else:
                debug("SERVER: DECRYPTION KEY FOR USER "+fromName+" IS CORRUPT")
        except TypeError:
            debug("SERVER: DATA CORRUPT")
        except IOError:
            debug("SERVER: UNKNOWN USER!")
        except KeyError:
            debug("SERVER: KEYERROR IN TEXT")
        debug("SERVER: DATA PROCCESED")

        #CHECK FOR CONNECT
        try:
            text = msg["connect"]
            f = open("keys/"+fromName+".decrypt", "r")
            receiveKey = tagsToDict(f.read())
            f.close()
            if not receiveKey == "DATAERROR":
                try:
                    #print msg["connect"]
                    if (rsaMod.decrypt(msg["connect"], receiveKey) == "connect"):
                        if not fromName in self.client.contactOnlineList:
                            # NOTIFY THE USER OF THE CONNECT
                            self.gui.displayMsg("has connected!", fromName)

                        # reply the connect msg! only if it is not myself
                        data = dict()
                        data["fromName"] = self.serverName
                        data["fromIP"] = self.serverHost + ":" + str(self.serverPort)
                        data["connect"] = rsaMod.encrypt("reconnect", self.client.sendKey)
                        sendData(data, fromIP, fromPort)
                        #print "SERVER: REPLIED THE CONNECT SINGAL"
                        # ALWAYS ADD CONTACT TO LIST, MAYBE THE IP OR PORT HAS CHANGED!
                        if len(fromName) > self.client.longestContact:
                            self.client.longestContact = len(fromName)
                        self.client.contactOnlineList[fromName] = fromIP + ":" + fromPort

                    if (rsaMod.decrypt(msg["connect"], receiveKey) == "reconnect"):

                        if not fromName in self.client.contactOnlineList:
                            # NOTIFY THE USER OF THE CONNECT
                            self.gui.displayMsg("has connected!", fromName)
                            #print "SERVER: REPLIED THE CONNECT SINGAL"
                        # ALWAYS ADD CONTACT TO LIST, MAYBE THE IP OR PORT HAS CHANGED!
                        if len(fromName) > self.client.longestContact:
                            self.client.longestContact = len(fromName)
                        self.client.contactOnlineList[fromName] = fromIP + ":" + fromPort



                except KeyError:
                    debug("SERVER: DATA IS CORRUPT, CAN NOT decrypt")
            else:
                debug("SERVER: DECRYPTION KEY FOR USER "+fromName+" IS CORRUPT")
        except TypeError:
            debug("SERVER: DATA CORRUPT")
        except IOError:
            debug("SERVER: UNKNOWN USER!")
        except KeyError:
            debug("SERVER: KEYERROR IN CONTACTS")
        debug("SERVER: DATA PROCCESED")






            
## LOAD GUI

class Gui():
    def __init__(self, client, master):
        self.client = client
        print self.client.contactOnlineList
        self.queue = self.client.queue
        # Set up the GUI
        self.app = master
        self.app.title("EncryptedChat")
        self.app.bind_all('<Key>', self.keypress)
        self.app.outputTextBox = ScrolledText.ScrolledText()
        self.app.outputTextBox.grid()
        self.app.inputTextBox = Entry(width=110)
        self.app.inputTextBox.grid()
        self.app.quitButton = Button ( text='Send', command=self.sendMsg )
        self.app.quitButton.grid()
 
        # Add more GUI stuff here
    def sendMsg(self):
        msg = self.app.inputTextBox.get()
        self.app.inputTextBox.delete(0,END)
        if msg == "":
            self.displayText("CLIENT: CANNOT SEND EMPTY MESSAGES")
            return

        print "contactOnlineList = "+str(self.client.contactOnlineList)
        if msg.startswith("#"):
            self.client.handleCommand(msg)
        else:
            # find out if there is a contact selected
            if self.client.selectedContact == "":
                print "CLIENT: NO CONTACT SELECTED, PLEASE SELECT ONE"

            elif self.client.selectedContact == "*":
                for x in self.client.contactOnlineList:
                    toIP, toPort = splitChar(self.client.contactOnlineList[x])
                    data = dict()
                    data["fromName"] = self.client.clientName
                    data["fromIP"] = self.client.serverIP + ":" + str(self.client.serverPort)
                    data["text"] = rsaMod.encrypt(msg, self.client.sendKey)

                    sendData(data, toIP, toPort)

                self.displayMsg(msg, clientName, True)
            else:
                    # SEND TO 1 PERSON SPECIAL
                try:
                    toIP, toPort = splitChar(self.client.contactOnlineList[selectedContact])
                except KeyError:
                    print "CLIENT: UNKNOWN CONTACT"
                    return

                    # send the msg
                data = dict()
                data["fromName"] = self.client.clientName
                data["fromIP"] = self.client.serverIP + ":" + str(self.client.serverPort)
                data["text"] = rsaMod.encrypt(msg, self.client.sendKey)

                sendData(data, toIP, toPort)
                self.displayMsg(msg, self.client.clientName, True)

    def displayText(self, text):
        self.queue.put(text)

    def displayMsg(self, msg, name, backtalkAllowed=False):
        if (not name == self.client.clientName):
            # if you arent talking to yourself
            output = "<"+name+">"
            print "LONGEST CONTACT = "+str(self.client.longestContact)
            for x in range(len(output), self.client.longestContact + 4):
                output += " "

            output += msg
            self.displayText(output)

        elif backtalkAllowed:
            # if you arent talking to yourself
            output = "<"+name+">"
            print "LONGEST CONTACT = "+str(self.client.longestContact)
            for x in range(len(output), self.client.longestContact + 4):
                output += " "

            output += msg
            self.displayText(output)

    def outputText(self, text):
        self.app.outputTextBox.insert(END, text+"\n")
        self.app.outputTextBox.see(END)
                    #data["connect"] = "connect"
    def keypress(self, event):
        if(event.keysym == "Return"):
            self.sendMsg()
    def processIncoming(self):
        """
        Handle all the messages currently in the queue (if any).
        """
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                # Check contents of message and do what it says
                # As a test, we simply print it
                print "THIS IS THE MSG" + msg
                self.outputText(msg)
            except Queue.Empty:
                pass

def sendData(msg, toIP, toPort):
    # FORM THE MSG

    data = dictToTags(msg)

    addr = (toIP, int(toPort))
    print "SEND: "+str(data) + str(addr)
    # SEND THE MSG
    socketClient = socket(AF_INET, SOCK_DGRAM)
    if(socketClient.sendto(data,addr)):
        socketClient.close()
        return True
         #print "SENDING MSG "+str(data)+" TO "+str(addr)
class client():
    def __init__ ( self, master):
        self.serverPort = serverPort
        self.serverIP = serverIP
        self.clientName = clientName
        self.queue = Queue.Queue()
        self.master = master
        self.selectedContact = "*"
        self.contactOnlineList = {}
        self.longestContact = 10
        self.gui = Gui(self, master)
        self.periodicCall()

        self.run()

    def sendConnectSignals(self, ipList, clientName, serverIP, serverPort, sendKey):
        for x in ipList:
            data = dict()
            data["fromName"] = clientName
            data["fromIP"] = serverIP + ":" + str(serverPort)
            data["connect"] = rsaMod.encrypt("connect", sendKey)
            host, port = splitChar(ipList[x])
            sendData(data, host, port)
            
    def handleCommand(self, text):
        text = text[1:]
        command = text.split(" ")[0]

        if command.startswith("list"):
            self.gui.displayText("CLIENT: ONLINE CONTACTS")
            for x in self.contactOnlineList:
                self.gui.displayText(x)
        elif command.startswith("select"):
            try:
                if text.split(" ")[1] in self.contactOnlineList:
                    self.selectedContact = text.split(" ")[1]
                    self.gui.displayText("CLIENT: CONTACT "+selectedContact+" SELECTED")

                elif text.split(" ")[1] == "*":
                    self.selectedContact = text.split(" ")[1]
                    self.gui.displayText("CLIENT: CONTACT EVERYONE IS SELECTED")
                else:
                    self.gui.displayText("CLIENT: CONTACT UNKNOWN")
            except IndexError:
                self.gui.displayText("CLIENT: "+self.selectedContact+" IS SELECTED")

    

    def periodicCall(self):
        """
        Check every 100 ms if there is something new in the queue.
        """
        self.gui.processIncoming()
        self.master.after(100, self.periodicCall)

    def run(self):
        print "RUN!"

        #loading ip's
        ipList = dict()
        f = open("ip.list", "r")
        n = 0
        for line in f.readlines():
            line = line.replace("\n", "")
            n += 1
            try:
                ip, port = splitChar(line)
                ipList[n] = str(ip) + ":" + port

            except:
                self.gui.displayText("GLOBAL: WARNING IPLIST IS CORRUPT")
        f.close()

        print "IPLIST = "+str(ipList)
        self.gui.displayText("GLOBAL: IP'S LOADED")


        # LOAD MY KEY
        f = open("keys/"+clientName+".encrypt", "r")
        self.sendKey = tagsToDict(f.read())
        f.close()
        self.gui.displayText("GLOBAL: ENCRYPTION KEY LOADED")


        # STARTING THE RECEIVING SERVER
        server(self, self.gui).start()

        time.sleep(2)

        # START TRYING ONLINE PPL
        self.gui.displayText("GLOBAL: START SENDING THE CONNECT SIGNALS")

        #print ipList
        self.sendConnectSignals(ipList, self.clientName, self.serverIP, self.serverPort, self.sendKey)

        # GET THE CONTACT LIST
        self.gui.displayText("CLIENT: READY")


# LOADING THE IP's

root = Tk()
main = client(root)
root.mainloop()
quit()

                
    
    
